// Copyright 2021 Guoyoko. All Rights Reserved.

#include "SWGameEngine.h"
#include "GameMapsSettings.h"
#include "IMovieSceneCapture.h"
#include "MovieSceneCaptureModule.h"
#include "Slate/SGameLayerManager.h"
#include "Slate/SceneViewport.h"
#include "GameFramework/GameUserSettings.h"
#include "Engine/GameViewportClient.h"

#if WITH_EDITOR
#include "PIEPreviewDeviceProfileSelectorModule.h"
#include "IPIEPreviewDeviceModule.h"
#endif


void USWGameEngine::CreateGameViewportWidget(UGameViewportClient* GameViewportClient)
{
	bool bRenderDirectlyToWindow = (!StartupMovieCaptureHandle.IsValid() || IMovieSceneCaptureModule::Get().IsStereoAllowed()) && GIsDumpingMovie == 0;

	TSharedRef<SOverlay> ViewportOverlayWidgetRef = SNew(SOverlay);

	TSharedRef<SGameLayerManager> GameLayerManagerRef = SNew(SGameLayerManager)
		.SceneViewport_UObject(this, &USWGameEngine::GetGameSceneViewport, GameViewportClient)
		[
			ViewportOverlayWidgetRef
		];

	// when we're running in a "device simulation" window, render the scene to an intermediate texture
	// in the mobile device "emulation" case this is needed to properly position the viewport (as a widget) inside its bezel
#if WITH_EDITOR
	auto PIEPreviewDeviceModule = FModuleManager::LoadModulePtr<IPIEPreviewDeviceModule>("PIEPreviewDeviceProfileSelector");
	if (PIEPreviewDeviceModule && FPIEPreviewDeviceModule::IsRequestingPreviewDevice())
	{
		bRenderDirectlyToWindow = false;
		PIEPreviewDeviceModule->SetGameLayerManagerWidget(GameLayerManagerRef);
	}
#endif

	const bool bStereoAllowed = bRenderDirectlyToWindow;

	bRenderDirectlyToWindow = false;

	TSharedRef<SViewport> GameViewportWidgetRef =
		SNew(SViewport)
		// Render directly to the window backbuffer unless capturing a movie or getting screenshots
		// @todo TEMP
		.RenderDirectlyToWindow(bRenderDirectlyToWindow)
		//gamma handled by the scene renderer
		.EnableGammaCorrection(false)
		.EnableStereoRendering(bStereoAllowed)
		[
			GameLayerManagerRef
		];

	GameViewportWidget = GameViewportWidgetRef;


	GameViewportClient->SetViewportOverlayWidget(GameViewportWindow.Pin(), ViewportOverlayWidgetRef);
	GameViewportClient->SetGameLayerManager(GameLayerManagerRef);
}

void USWGameEngine::CreateGameViewport(UGameViewportClient* GameViewportClient)
{
	check(GameViewportWindow.IsValid());

	if (!GameViewportWidget.IsValid())
	{
		CreateGameViewportWidget(GameViewportClient);
	}

	TSharedRef<SViewport> GameViewportWidgetRef = GameViewportWidget.ToSharedRef();

	auto Window = GameViewportWindow.Pin();

	Window->SetOnWindowClosed(FOnWindowClosed::CreateUObject(this, &UGameEngine::OnGameWindowClosed));

	// SAVEWINPOS tells us to load/save window positions to user settings (this is disabled by default)
	int32 SaveWinPos;
	if (FParse::Value(FCommandLine::Get(), TEXT("SAVEWINPOS="), SaveWinPos) && SaveWinPos > 0)
	{
		// Get WinX/WinY from GameSettings, apply them if valid.
		FIntPoint PiePosition = GetGameUserSettings()->GetWindowPosition();
		if (PiePosition.X >= 0 && PiePosition.Y >= 0)
		{
			int32 WinX = GetGameUserSettings()->GetWindowPosition().X;
			int32 WinY = GetGameUserSettings()->GetWindowPosition().Y;
			Window->MoveWindowTo(FVector2D(WinX, WinY));
		}
		Window->SetOnWindowMoved(FOnWindowMoved::CreateUObject(this, &UGameEngine::OnGameWindowMoved));
	}

	SceneViewport = MakeShareable(new FSceneViewport(GameViewportClient, GameViewportWidgetRef));
	GameViewportClient->Viewport = SceneViewport.Get();
	//GameViewportClient->CreateHighresScreenshotCaptureRegionWidget(); //  Disabled until mouse based input system can be made to work correctly.

	// The viewport widget needs an interface so it knows what should render
	GameViewportWidgetRef->SetViewportInterface(SceneViewport.ToSharedRef());

	FSceneViewport* ViewportFrame = SceneViewport.Get();

	GameViewport->SetViewportFrame(ViewportFrame);

	GameViewport->GetGameLayerManager()->SetSceneViewport(ViewportFrame);

	FViewport::ViewportResizedEvent.AddUObject(this, &UGameEngine::OnViewportResized);
}

void USWGameEngine::Init(IEngineLoop* InEngineLoop)
{
	DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGameEngine Init"), STAT_GameEngineStartup, STATGROUP_LoadTime);


	// Call base.
	UEngine::Init(InEngineLoop);

//#if USE_NETWORK_PROFILER
//	FString NetworkProfilerTag;
//	if (FParse::Value(FCommandLine::Get(), TEXT("NETWORKPROFILER="), NetworkProfilerTag))
//	{
//		GNetworkProfiler.EnableTracking(true);
//	}
//#endif

	// Load and apply user game settings
	GetGameUserSettings()->LoadSettings();
	GetGameUserSettings()->ApplyNonResolutionSettings();

	// Create game instance.  For GameEngine, this should be the only GameInstance that ever gets created.
	{
		FSoftClassPath GameInstanceClassName = GetDefault<UGameMapsSettings>()->GameInstanceClass;
		UClass* GameInstanceClass = (GameInstanceClassName.IsValid() ? LoadObject<UClass>(NULL, *GameInstanceClassName.ToString()) : UGameInstance::StaticClass());

		if (GameInstanceClass == nullptr)
		{
			//UE_LOG(LogEngine, Error, TEXT("Unable to load GameInstance Class '%s'. Falling back to generic UGameInstance."), *GameInstanceClassName.ToString());
			GameInstanceClass = UGameInstance::StaticClass();
		}

		GameInstance = NewObject<UGameInstance>(this, GameInstanceClass);

		GameInstance->InitializeStandalone();
	}

	//  	// Creates the initial world context. For GameEngine, this should be the only WorldContext that ever gets created.
	//  	FWorldContext& InitialWorldContext = CreateNewWorldContext(EWorldType::Game);

//	IMovieSceneCaptureInterface* MovieSceneCaptureImpl = nullptr;
//#if WITH_EDITOR
//	if (!IsRunningDedicatedServer() && !IsRunningCommandlet())
//	{
//		MovieSceneCaptureImpl = IMovieSceneCaptureModule::Get().InitializeFromCommandLine();
//		if (MovieSceneCaptureImpl)
//		{
//			StartupMovieCaptureHandle = MovieSceneCaptureImpl->GetHandle();
//		}
//	}
//#endif

	// Initialize the viewport client.
	UGameViewportClient* ViewportClient = NULL;
	if (GIsClient)
	{
		ViewportClient = NewObject<UGameViewportClient>(this, GameViewportClientClass);
		ViewportClient->Init(*GameInstance->GetWorldContext(), GameInstance);
		GameViewport = ViewportClient;
		GameInstance->GetWorldContext()->GameViewport = ViewportClient;
	}

	//LastTimeLogsFlushed = FPlatformTime::Seconds();

	// Attach the viewport client to a new viewport.
	if (ViewportClient)
	{
		// This must be created before any gameplay code adds widgets
		bool bWindowAlreadyExists = GameViewportWindow.IsValid();
		if (!bWindowAlreadyExists)
		{
			//UE_LOG(LogEngine, Log, TEXT("GameWindow did not exist.  Was created"));
			GameViewportWindow = CreateGameWindow();
		}

		CreateGameViewport(ViewportClient);

		if (!bWindowAlreadyExists)
		{
			SwitchGameWindowToUseGameViewport();
		}

		FString Error;
		if (ViewportClient->SetupInitialLocalPlayer(Error) == NULL)
		{
			//UE_LOG(LogEngine, Fatal, TEXT("%s"), *Error);
		}

		UGameViewportClient::OnViewportCreated().Broadcast();
	}

	UE_LOG(LogInit, Display, TEXT("Game Engine Initialized."));

	// for IsInitialized()
	bIsInitialized = true;
}


void USWGameEngine::Tick(float DeltaSeconds, bool bIdleMode)
{
	Super::Tick(DeltaSeconds, bIdleMode);
}

FSceneViewport* USWGameEngine::GetGameSceneViewport(UGameViewportClient* ViewportClient) const
{
	return ViewportClient->GetGameViewport();
}

